package cn.fengtang.chatglm.interfaces;

import cn.bugstack.chatglm.model.*;
import cn.bugstack.chatglm.session.Configuration;
import cn.bugstack.chatglm.session.OpenAiSession;
import cn.bugstack.chatglm.session.OpenAiSessionFactory;
import cn.bugstack.chatglm.session.defaults.DefaultOpenAiSessionFactory;
import cn.fengtang.chatglm.application.IWeiXinValidateService;
import cn.fengtang.chatglm.domain.receive.model.MessageTextEntity;
import cn.fengtang.chatglm.infrastructure.util.XmlUtil;
import okhttp3.logging.HttpLoggingInterceptor;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

/**
 * @author 小傅哥，微信：fustack
 * @description 微信公众号，请求处理服务
 * @github https://github.com/fuzhengwei
 * @Copyright 公众号：bugstack虫洞栈 | 博客：https://bugstack.cn - 沉淀、分享、成长，让自己和他人都能有所收获！
 */
@RestController
@RequestMapping("/wx/portal/{appid}")
public class WeiXinPortalController {

    private final Logger logger = LoggerFactory.getLogger(WeiXinPortalController.class);

    private static final int MAX_RETRY = 3;
    private static final int FIRST_WAIT_SECONDS = 5;
    private static final int SECOND_WAIT_SECONDS = 5;
    private static final int FINAL_WAIT_SECONDS = 4;

    @Value("${wx.config.originalid:gh_e38625f832e2}")
    private String originalId;

    @Resource
    private IWeiXinValidateService weiXinValidateService;

    private final OpenAiSession openAiSession;

    @Resource
    private ThreadPoolTaskExecutor taskExecutor;

    //存放openAi返回结果数据
    private final Map<String, String> openAiDataMap = new ConcurrentHashMap<>();
    //存放openAi调用次数数据
    private final Map<String, Integer> openAiRetryCountMap = new ConcurrentHashMap<>();

    public WeiXinPortalController() {
        //1.配置文件
        Configuration configuration = new Configuration();
        configuration.setApiHost("https://open.bigmodel.cn/");
        configuration.setApiSecretKey("02520c59d73b4c2bbb8a0614ba7ffb17.dFHZX4765lAHutq1");
        configuration.setLevel(HttpLoggingInterceptor.Level.BODY);
        //2.会话工厂
        OpenAiSessionFactory factory = new DefaultOpenAiSessionFactory(configuration);
        //3.开启会话
        this.openAiSession = factory.openSession();
    }

    /**
     * 处理微信服务器发来的get请求，进行签名的验证
     * https://5da19210.r28.cpolar.top/wx/portal/wx30b177160af4a746
     * <p>
     * appid     微信端AppID
     * signature 微信端发来的签名
     * timestamp 微信端发来的时间戳
     * nonce     微信端发来的随机字符串
     * echostr   微信端发来的验证字符串
     */
    @GetMapping(produces = "text/plain;charset=utf-8")
    public String validate(@PathVariable String appid,
                           @RequestParam(value = "signature", required = false) String signature,
                           @RequestParam(value = "timestamp", required = false) String timestamp,
                           @RequestParam(value = "nonce", required = false) String nonce,
                           @RequestParam(value = "echostr", required = false) String echostr) {

        try {
            logger.info("微信验证验签信息{}开始，[{},{},{},{}]", appid, signature, timestamp, nonce, echostr);
            if (StringUtils.isAnyBlank(signature, timestamp, nonce, echostr)) {
                throw new IllegalArgumentException("请求参数非法，请核实！");
            }
            boolean check = weiXinValidateService.checkSign(signature, timestamp, nonce);
            logger.info("微信公众号验签信息{}完成 check：{}", appid, check);
            if (!check) {
                return null;
            }
            return echostr;
        } catch (Exception e) {
            logger.error("微信公众号验签信息{}失败 [{}, {}, {}, {}]", appid, signature, timestamp, nonce, echostr, e);
            return null;
        }
    }

    /**
     * 处理微信服务器的消息转发
     */
    @PostMapping(produces = "application/xml; charset=UTF-8")
    public String post(@PathVariable String appid,
                       @RequestBody String requestBody,
                       @RequestParam("signature") String signature,
                       @RequestParam("timestamp") String timestamp,
                       @RequestParam("nonce") String nonce,
                       @RequestParam("openid") String openid,
                       @RequestParam(name = "encrypt_type", required = false) String encType,
                       @RequestParam(name = "msg_signature", required = false) String msgSignature) {

        logger.info("接收微信公众号信息请求{}开始 {}", openid, requestBody);

        try {
            MessageTextEntity message = XmlUtil.xmlToBean(requestBody, MessageTextEntity.class);
            String content = message.getContent().trim();

            int retryCount = openAiRetryCountMap.getOrDefault(content, 0);
            logger.info("请求次数：{}", retryCount + 1);

            String aiResult = openAiDataMap.get(content);

            if (aiResult == null || "NULL".equals(aiResult)) {
                // 首次或处理中
                if (retryCount == 0 && aiResult == null) {
                    doChatGLMTask(content);
                }
                retryCount = Math.min(retryCount + 1, MAX_RETRY);
                openAiRetryCountMap.put(content, retryCount);

                int waitSeconds = getWaitSeconds(retryCount);
                logger.info("超时重试：{}，等待 {} 秒", retryCount, waitSeconds);
                TimeUnit.SECONDS.sleep(waitSeconds);

                aiResult = openAiDataMap.get(content);
                if (aiResult == null || "NULL".equals(aiResult)) {
                    aiResult = "消息处理中，请再回复我一句【" + message.getContent().trim() + "】";
                }
            } else {
                // 清理缓存
                openAiDataMap.remove(content);
                openAiRetryCountMap.remove(content);
            }

            MessageTextEntity res = buildTextResponse(openid, aiResult);
            String xmlResult = XmlUtil.beanToXml(res);
            logger.info("接收微信公众号信息请求{}完成 {}", openid, xmlResult);
            return xmlResult;
        } catch (Exception e) {
            logger.error("接收微信公众号信息请求{}失败 {}", openid, requestBody, e);
            return "";
        }
    }

    private static int getWaitSeconds(int retryCount) {
        if (retryCount == 1) return FIRST_WAIT_SECONDS;
        if (retryCount == 2) return SECOND_WAIT_SECONDS;
        return FINAL_WAIT_SECONDS;
    }

    private MessageTextEntity buildTextResponse(String openid, String content) {
        MessageTextEntity res = new MessageTextEntity();
        res.setToUserName(openid);
        res.setFromUserName(originalId);
        res.setCreateTime(String.valueOf(System.currentTimeMillis() / 1000L));
        res.setMsgType("text");
        res.setContent(content);
        return res;
    }

    public void doChatGLMTask(String content) {
        openAiDataMap.put(content, "NULL");
        taskExecutor.execute(() -> {
            ChatCompletionRequest request = new ChatCompletionRequest();
            request.setModel(Model.GLM_3_5_TURBO);
            request.setPrompt(new ArrayList<ChatCompletionRequest.Prompt>() {
                private static final long serialVersionUID = -7988151926241837899L;

                {
                    add(ChatCompletionRequest.Prompt.builder()
                            .role(Role.user.getCode())
                            .content(content)
                            .build());
                }
            });
            try {
                // 同步获取结果
                ChatCompletionSyncResponse response = openAiSession.completionsSync(request);
                // 保存数据
                openAiDataMap.put(content, response.getChoices().get(0).getMessage().getContent());
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        });
    }
}
